//
//  AtomicSpread.m
//  SujetObserver
//
//  Created by Pierre DUCHENE on 14/02/10.
//  Copyright 2010 Pierre DUCHENE. All rights reserved.
//

#import "AtomicSpread.h"
#import "ObserverProtocol.h"
#import "SensorData.h"
#import "SensorProtocol.h"

@implementation AtomicSpread

#pragma mark -
#pragma mark Memory Management
- (id) init
{
	self = [super init];
	if (self != nil) {
		queueForSetValue = [[NSOperationQueue alloc] init];
		[updateQueue addObserver:self
					  forKeyPath:@"operations"
						 options:NSKeyValueObservingOptionNew
						 context:nil];
		lastDependencies = [[NSMutableArray alloc] init];
		newValueOperation = nil;
		savedData = nil;
	}
	return self;
}

- (void) dealloc
{	
	[queueForSetValue release];
	queueForSetValue = nil;
	
	[lastDependencies release];
	lastDependencies = nil;
	
	[super dealloc];
}

#pragma mark -
#pragma mark SpreadProtocol Implementation

- (void)execute
{
	// Create an operation to set the new value of the sensor
	[newValueOperation release];
	SensorData* newData = [[[sensor dataForContext] copy] autorelease];
	newValueOperation = [[NSInvocationOperation alloc] initWithTarget:self
															 selector:@selector(setSensorData:)
															   object:newData];
	// If stil remain other operations, we have to save the order
	// So we make a dependency with remain operations
	BOOL needDependency = [[updateQueue operations] count] > 0;
	if (needDependency) {
		for (NSOperation* o in lastDependencies) {
			[newValueOperation addDependency:o];
		}
	}
	
	// Create all operations for updates observers
	NSSet* observers = [sensor getAllObservers];
	NSInvocationOperation* io = nil;
	for (id<ObserverProtocol> o in observers) {
		io = [[NSInvocationOperation alloc] initWithTarget:o
												  selector:@selector(update:)
													object:sensor];
		// Each update operation must wait until the new value will set
		[io addDependency:newValueOperation];
		[updateQueue addOperation:io];
		[lastDependencies addObject:io];
		[io release];
		io = nil;
	}
	
	// Finaly add the operation to change the value
	[queueForSetValue addOperation:newValueOperation];
}

- (SensorData*)sensorData
{
	return savedData;
}

#pragma mark -
#pragma mark Private Methods
- (void)setSensorData:(SensorData*)newData
{
	[savedData release];
	savedData = [newData retain];
}

#pragma mark -
#pragma mark Observing Implementation
- (void)observeValueForKeyPath:(NSString *)keyPath
					  ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
	if ([(NSArray*)[change objectForKey:NSKeyValueChangeNewKey] count] == 0) {
		[lastDependencies removeAllObjects];
	}
}
@end
